import shlex
import subprocess
from abc import ABC, abstractmethod
from typing import ClassVar, Dict, Type, List

from config import path_test
from exceptions.unzip_excepotion import NotSoftware
from implement.unzip.BandizipCompressor import BandizipCompressor
from implement.unzip.BandizipDecompressor import BandizipDecompressor
from implement.unzip.SevenZipCompressor import SevenZipCompressor
from implement.unzip.SevenZipDecompressor import SevenZipDecompressor
from implement.unzip.WinRarCompressor import WinRarCompressor
from implement.unzip.WinRarDecompressor import WinRarDecompressor
from interface.unzip_interfaces import CompressionTool, DecompressionTool
from utils.LogDecorator import LogDecorator

log = LogDecorator(name=__name__)


class CompressionToolFactory(ABC):
    """压缩工具工厂抽象基类"""

    @classmethod
    @abstractmethod
    def is_available(cls) -> bool:
        """检查工具是否可用"""
        pass

    @abstractmethod
    def create_compressor(self) -> CompressionTool:
        """创建压缩器实例"""
        pass

    @abstractmethod
    def create_decompressor(self) -> DecompressionTool:
        """创建解压器实例"""
        pass


class CompressionToolFactoryRegistry:
    """工厂注册中心（单例模式）"""
    _instance: ClassVar = None
    _factories: Dict[str, Type[CompressionToolFactory]] = {}

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._initialize_default_factories()
        return cls._instance

    @classmethod
    def _initialize_default_factories(cls):
        """初始化默认工厂"""
        cls.register_factory('bandizip', BandizipFactory)
        cls.register_factory('7zip', SevenZipFactory)
        cls.register_factory('winrar', WinRarFactory)

    @classmethod
    def register_factory(cls, name: str, factory: Type[CompressionToolFactory]):
        """注册新的工厂类型"""
        if not issubclass(factory, CompressionToolFactory):
            raise TypeError("Factory must be a subclass of CompressionToolFactory")
        cls._factories[name.lower()] = factory

    @classmethod
    def unregister_factory(cls, name: str):
        """取消注册工厂类型"""
        cls._factories.pop(name.lower(), None)

    @classmethod
    def get_available_factories(cls) -> List[Type[CompressionToolFactory]]:
        """获取所有已注册的工厂类型"""
        return list(cls._factories.values())


class BandizipFactory(CompressionToolFactory):
    @classmethod
    def is_available(cls) -> bool:
        command = f'bandizip t -y "{path_test}"'
        return JudgementSoftware.judgement(command)["success"]

    def create_compressor(self) -> CompressionTool:
        return BandizipCompressor()

    def create_decompressor(self) -> DecompressionTool:
        return BandizipDecompressor()


class SevenZipFactory(CompressionToolFactory):
    @classmethod
    def is_available(cls) -> bool:
        command = "7z"
        return JudgementSoftware.judgement(command)["success"]

    def create_compressor(self) -> CompressionTool:
        return SevenZipCompressor()

    def create_decompressor(self) -> DecompressionTool:
        return SevenZipDecompressor()


class WinRarFactory(CompressionToolFactory):
    @classmethod
    def is_available(cls) -> bool:
        command = "rar"
        return JudgementSoftware.judgement(command)["success"]

    def create_compressor(self) -> CompressionTool:
        return WinRarCompressor()

    def create_decompressor(self) -> DecompressionTool:
        return WinRarDecompressor()


class CompressionToolFactorySelector:
    """工厂选择器（策略模式）"""

    @classmethod
    def select_factory(cls) -> CompressionToolFactory:
        registry = CompressionToolFactoryRegistry()
        for factory_class in registry.get_available_factories():
            if factory_class.is_available():
                return factory_class()
        raise NotSoftware("No available compression software found.")


class JudgementSoftware:
    """工具可用性判断器"""

    def __init__(self):
        self._factory = CompressionToolFactorySelector.select_factory()

    @staticmethod
    def judgement(command: str) -> dict:
        """执行命令行判断"""
        try:
            log.debug(f"正在执行测试命令: {command}")
            command_list = shlex.split(command)
            subprocess.run(
                command_list,
                check=True,
                stdout=subprocess.DEVNULL,
                stderr=subprocess.DEVNULL
            )
            return {"success": True, "error": None}
        except (subprocess.CalledProcessError, OSError, ValueError) as e:
            return {"success": False, "error": str(e)}

    def get_compressor(self) -> CompressionTool:
        """获取压缩器实例"""
        return self._factory.create_compressor()

    def get_decompressor(self) -> DecompressionTool:
        """获取解压器实例"""
        return self._factory.create_decompressor()

    def __str__(self):
        return f"{self.__class__.__name__} [Using: {type(self._factory).__name__}]"


# 使用示例
if __name__ == '__main__':
    # 使用判断器
    judge = JudgementSoftware()
    print(judge.get_compressor())
    print(judge.get_decompressor())
